import java.io.* ;
import java.net.* ;
import java.util.* ;
/****************************************************************************************************************
* Assertive Speech over Sockets Server-side multi-threaded application.
* 
* The server acts as a chat room service by meditating communcations over TCP layer sockets.
* A thread affectionally labeled 'DoorMan' awaits for construction of TCP connections by jvm.
* New clients are put on a waitlist before they are provided services.
* A thread called Server processes all instructions of relaying information to clients and manages internal state.
* Server will spawn threads to take care of processes that do not require change of internal state.
* For each TCP socket constructed, there is a thread that blocks for input from the end user process.
* A Client thread will puts the request recieved on the Server's instruction buffer while the Server is busy processing instructions. 
***************************************************************************************************************/
public class ASSServer{
	/*<CONSTANT>*/
	/** Control character defined in RFC 3.14159 */
	public static final String CRLF="\r\n";
	/** Maximum size of a lower layer segment. */
	public static final int MAXBUFFERLENGTH=512;
	/** The server's welcome port number= -(2903). */
	public static final int WELCOMEPORT=7097;
	/** Greatest number of users in the chat room at a given time. */
	public static final int MAXCLIENTS=10;
	/** Greatest number of users in the waiting pool at a given time. */
	public static final int MAXWAITING=4096;
	/*</CONSTANT>*/
	
	/*<UNIQUE>*/
	/** Colon and EOL delimited text file of user information */
	private final String dataFile="keys.dat";
	/** Unique packet handling server process */
	private final Server server;
	/** Unique welcome server process */
	private final DoorMan doorman;
	/*</UNIQUE>*/
	
	/*<INSTANCE>*/
	/** Global operational status */
	public boolean running=true;
	/** General error messages for client processes. */
	public PrintStream stderr=System.out;
	/*</INSTANCE>*/
	
	/***************************************************************************
	 *  Driver method to boot a chat room
	 ***************************************************************************/
	public static void main(String args[]){
		ASSServer chatroom = new ASSServer();
	}/*</main>*/
	/**************************************************************************
	*  Get a chat room instance.
	**************************************************************************/
	public ASSServer(){
		server=new Server();
		doorman=new DoorMan();
		server.start();
		doorman.start();
	}/*</ASSServer>*/
	/***************************************************************************
	 * Process responsible for processing incoming packets and maintaining user
	 * connection state.
	 **************************************************************************/
	private class Server extends Thread{
		/*<UNIQUE>*/
		/***********************************************************************
		 * Messages that show up on the admin's screen relating to the server
		 * status.
		 **********************************************************************/
		final public PrintStream serverStream;
		/** user:password pairs. */
		final private ArrayList<Key> database;
		/** The server's instruction stack. */
		final private Buffer<ServerPacket>	stack;
		/** List of clients who are engaged in chat session */
		final private HostList<Client> party;
		/** List of clients waiting to chat */
		final private HostList<Client>	waitList;
		/** Return address for system messages to client. */
		private final Admin admin;
		/*</UNIQUE>*/
		/***********************************************************************
		 * Build one.
		 **********************************************************************/
		private Server(){
			admin=new Admin();
			serverStream=System.out;
			serverStream.println("\nSERVER> INITIALIZING...");
			party=new HostList<Client>(MAXCLIENTS);
			waitList =new HostList<Client>(MAXWAITING);
			stack = new Buffer<ServerPacket>();
			database=new ArrayList<Key>(10);
			getDatabase();
			running = true;
			serverStream.println("\nSERVER> ... INITIALIZATION SUCCESSFUL");
		}/*</Server>*/
		/***************************************************************************
		 * Fetches user data from un-encoded file, "user:password" on each line.
		 **************************************************************************/
		private void getDatabase(){
			BufferedReader fin;
			try{
				fin=new BufferedReader(new FileReader(dataFile));
				while(fin.ready()){
					String x[]=fin.readLine().split(":");
					database.add(new Key(x[0],x[1]));
				}
				System.out.println("\nSERVER> DATABASE LOADED SUCESSFULLY");
			}
			catch(Exception e){
				System.out.println("\nSERVER> CRITICAL: UNABLE TO LOCATE FILE");
			}
			//for(int i=0;i<database.size();i++)
				//serverStream.println(database.get(i).uid +" : "+database.get(i).password);
		}/*</getDatabase>*/
		/***************************************************************************
		 * Does the converse, overwrites the contents of the old database.
		 **************************************************************************/
		private void putDatabase(){
			FileWriter fout;
			try{
					fout=new FileWriter(dataFile);
					for(int i=0;i<database.size();i++){
						Key k=database.get(i);
						fout.write(k.uid+":"+k.password+"\n");
					}
					fout.close();
			}
			catch(Exception e){
				serverStream.println("\nSERVER> CRITICAL: UNABLE TO PUT DATABASE");
			}
		}/*</putDatabase>*/
		/***********************************************************************
		 * Need to make this method synchronized, or else constantly
		 * @throws IllegalMonitorState Exception.
		 **********************************************************************/
		public synchronized void run(){
			ServerPacket q;
			while(running){
				while((q=stack.deque())!=null){
					(new QueryHandler(q)).handle();
				}/*</while>*/
				try{wait();}/* wait until interrupt */
				catch(InterruptedException e){
					serverStream.println("SERVER> IRQ");
				}
				catch(Exception e){
					serverStream.println("SERVER> ERROR ENCOUNTERED");
					e.printStackTrace(serverStream);
				}
			}/*</while>*/
		}/*</run>*/
		/***********************************************************************
		 * For client processes to push things onto.
		 * synchronization issue?
		 **********************************************************************/
		public boolean push(ServerPacket p){
			boolean x=stack.queue(p);
			server.interrupt();
			return x;
		}/*</push>*/
		/***********************************************************************
		 * Client calls this to put himself into waitlist.
		 * synchronization issue?
		 **********************************************************************/
		public boolean push(Client c){
			serverStream.println(
				"SERVER> PUSH CALLED for "+c.toString());
			boolean x=waitList.queue(c);
			return x;
		}/*</push>*/
		/***********************************************************************
		 * All the code for dealing with the incoming packet.
		 *	{login,logout,register} - require server attention.
		 *	Second issue is two users trying to log in with same username 
		 *		  at nearly same time may (almost) be able to get away with it
		 *	{who,send,sendall} - can be passed off to lesser dying process
		 ***********************************************************************/
		private class QueryHandler extends Thread{
			/***********************************************************************
			 * The thing this method was called with.
			 **********************************************************************/
			private final ServerPacket query;
			private final String command;
			private final String uid;
			private final String body;
			private final Client client;
			/***********************************************************************
			 * Assign the variable
			 **********************************************************************/
			private QueryHandler(ServerPacket q){
				query=q;
				command=q.command;
				uid=q.uid;
				body=q.body;
				client=q.c;
			}/*</QueryHandler>*/
			/***********************************************************************
			 * The only method used.  Decides whether to use the server's thread
			 * for safe access methods, or to start a throw away thread.
			 **********************************************************************/
			public void handle(){
				serverStream.println(
					"\nSERVER> PROCESSING QUERY\n"
					+ query.toDelimitedString()
				);
				if(command.equals("login"))login();
				else if(command.equals("logout"))logout();
				else if(command.equals("register"))register();
				else this.run();
			}
			public void run(){
				if(command.equals("disconnect")){
					disconnect();
					return;
				}
				if(!priveledged())
					return;
				if(command.equals("send"))send();
				else if(command.equals("sendall"))sendall();
				else if(command.equals("who"))who();
				else illegal();/* must be illegal command */
			}/*</run>*/
			/***********************************************************
			* Ensure client is removed from party and waitlist,
			* call method to kill listening thread.
			************************************************************/
			private void disconnect(){
				party.pull(client);
				waitList.pull(client);
				client.kill();
			}/*</disconnect>*/
			private void login(){
				if(party.get(uid)!=null){ // prevent two users with same uid logging simultaneously
					client.deauthenticate( "ERROR: <"+ uid + "> IS ALREADY LOGGED IN");
					return;
				}
				if(party.get(client.uid)!=null){ // prevent client from logging in while already logged in
					client.transmit(new Packet("admin","login","You are already logged in, please logout first"));
					return;
				}
				if(!passwd()){  // checks uid:password against database
					serverStream.println(
						"\nSERVER> PAIR NOT FOUND"
						+"<"+uid+">"+"<"+body+">"
					);
					client.deauthenticate("PAIR NOT FOUND");
					return;
				}
				if(party.shift(waitList,client)){ // attempt to move client from waitlist to party
					client.authenticate(uid);
					serverStream.println("\nSERVER> "+client+" HAS JOINED");
					push(new ServerPacket("sendall","",uid+" HAS JOINED THE PARTY",admin));
				}
				else if(party.atCapacity()) // first reason for failure to do so
					client.deauthenticate(
						"DENIAL OF SERVICE: THE PARTY IS FULL, PLEASE TRY AGAIN LATER");
				else{ // unkown reason for failure
					serverStream.println(
						"SERVER> <"+uid+">"+client+" FAILED TO LOGIN FATALLY!");
					client.deauthenticate(
						"ERROR: AN UNKNOW ERROR OCCURED");
					// force client to logout, or perhaps to disconnect
					// warning: logic suggests this may deadlock,
						// thread may not be able to interrupt itself
					server.push(
						new ServerPacket(
							new Packet("logout","admin","<"+uid
							+">could not be added to waitlist"),client
						)
					);
				}
				serverStream.println("\nSERVER> LOGIN QUERY PROCESSED");
			}/*</login>*/
			/*******************************************************************
			* Be moved out of the party, onto the waitlist. Disconnected if
			* waitlist is full.
			********************************************************************/
			private void logout(){
				/* make sure the client gets to waitlist
					regardless of their initial state */
				if(party.get(client)!=null){
					if(waitList.shift(party,client)){
						final String id=client.uid;
						client.deauthenticate("YOU HAVE BEEN LOGGED OUT");
						party.broadcast(new Packet("admin","sendall","<"+id+"> left the party"));
					}
					else
						push(
							new ServerPacket(
								new Packet("disconnect","admin",
									"The waitlist cannot suppport anymore connections.")
								,client)
						);
				}
				else if(waitList.get(client)!=null){
					client.deauthenticate("YOU HAVE BEEN LOGGED OUT");
				}
				else // careful, might deadlock due to thread interrupting itself
					push(
						new ServerPacket(
							new Packet("disconnect","admin",
								"The waitlist cannot suppport anymore connections.")
							,client)
					);
			}/*</logout>*/
			/** Create a login identity. */
			private void register(){
				if(usrname()){
					client.transmit(new Packet("register","admin","FAILED USERNAME ALREADY EXISTS"));;
					return;
				}
				if(!verify(uid,body)){
					client.transmit(new Packet("register","admin","FAILED: INVALID USERNAME OR PASSWORD"));;
					return;
				}
				database.add(new Key(uid,body));
				putDatabase();
				client.transmit(new ServerPacket("register","admin","SUCESS: USER <"+uid+"> MAY NOW LOGIN ",admin));
			}/*</register>*/
			/** Priveledged to send a message to the party. */
			private boolean priveledged(){
				serverStream.println("\nSERVER> CHECKING PRIVELEDGES "+client+" : "+client.uid);
				// serverStream.println("\n\t"+query.toDelimitedString());
				if(client==admin || party.get(client)!=null)
					return true;
				client.deauthenticate();
				return false;
			}/*<priveledged>*/
			private void send(){
				Client recipient;
				if((recipient=party.get(uid))==null){
					client.transmit(new Packet("mail","admin",uid+" is not logged in"));
					return;
				}
				recipient.transmit(new Packet("mail",client.uid(),body));
				client.transmit(query.toPacket());
			}/*</send>*/
			private void sendall(){
				serverStream.println(
					"\nSERVER> SENDALL HANDLING QUERY"
					+"\n*************************************************\n"
					+ query.toDelimitedString()
					+"\n*************************************************\n"
				);
				party.broadcast(new Packet("sendall",client.uid(),body));
			}/*</sendall>*/
			private void who(){
				client.transmit(new Packet("who","admin",party.whoison()));;
			}/*</who>*/
			private void illegal(){
				serverStream.println(
					"\nSERVER> USER ATTEMPTED ILLEGAL COMMAND\n"
					+ query.toDelimitedString()
				);
				client.transmit(
					new Packet("mail","admin","ERROR: <"+ query.command + "> COMMAND NOT SUPPORTED")
				);
			}/*</illegal>*/
			/***********************************************************************
			 * Checks whether uid:password pair is in the database.
			 **********************************************************************/
			private boolean passwd(){
				for(int i=0;i<database.size();i++)
					if(query.uid.equals(database.get(i).uid))
						if(query.body.equals(database.get(i).password))
							return true;
						else
							break;
				return false;
			}/*</passwd>*/
			/***********************************************************************
			 * Checks whether uid is in the database.
			 **********************************************************************/
			private boolean usrname(){
				for(int i=0;i<database.size();i++)
					if(query.uid.equals(database.get(i).uid))
						return true;
				return false;
			}/*</usrname>*/
			/***********************************************************************
			* Decide whether password and uid are legal .
			/***********************************************************************/
			private boolean verify(String uid,String password){
				return (!(uid.equals("admin")))&&goodWord(uid)&&goodWord(password);
			}/*</verify>*/
			/***********************************************************************
			* Criteria for password and uid words.  Just masks off a the control
			* chars and other non-letter/number symbols.  Length not to exceed 14.
			***********************************************************************/
			private boolean goodWord(String word){
				byte[] a=word.getBytes();
				for(int i=0;i<word.length();i++)
					if(a[i]<43||(a[i]>57&&a[i]<65)||a[i]>122||i>14)
						return false;
				return true;
			}/*</goodWord>*/
		}/*</QueryHandler>*/
		/*******************************************************************
		 * Contains methods relating to members.
		 * Most are synchronized to hopefully avoid access conflicts
		 ******************************************************************/
		private class HostList<T extends Client> extends Buffer<T>{
			private final int CAPACITY;
			private HostList(int cap){
				super(cap);
				CAPACITY=cap;
			}/*</HostList>*/
			/***************************************************************
			 * Constructors are considered synchronized.
			 **************************************************************/
			private HostList(){
				super(4096*2);
				CAPACITY=4096*2;
			}/*</HostList>*/
			/***************************************************************
			 * Each client must have unique port, uid, and instance
			 * @return Whether insertion successful, most likely will fail 
			 * because client found in the list.
			 * @override
			 **************************************************************/
			public synchronized boolean queue(T c){
				/*serverStream.println(
					"MAIN> queue CALLED for "+c.toString());*/
				if(super.get(c)==null) /* then we can insert him */
					return super.queue(c);
				return false;
			}/*</queue>*/
			/***************************************************************
			 * Remove the client with respective port form the list.
			 * @return The guy pulled. null if not located.
			 **************************************************************/
			private synchronized T pull(int port){
				for(int i=0;i<line.size();i++)
					if(port==line.get(i).port())
						return line.remove(i);
				return null;
			}/*</pull>*/
			/***************************************************************
			 * Remove the client with respective uid form the list.
			 * @return The guy pulled. null if not located.
			 **************************************************************/
			synchronized private T pull(String uid){
				for(int i=0;i<line.size();i++)
					if(uid.equals(line.get(i).uid()))
						return line.remove(i);
				return null;
			}/*</deque>*/
			/***************************************************************
			 * Remove the client with respective object reference from list.
			 * @return The guy pulled. null if not located.
			 **************************************************************/
			synchronized private T pull(Client c){
				for(int i=0;i<line.size();i++)
					if(c==line.get(i))
						return line.remove(i);
				return null;
			}/*</pull>*/
			/***************************************************************
			 *  Guarantee the client is in this list, if neccessary, move
			 *  from the other list to this list.  Return false if unable to
			 *  make it possible, perhaps if the client disconnected 
			 *  from server.
			 * @param otherList
			 * @param port client's port no
			 * @return Whether exchange successful, failure most likely
			 * due to over population.
			 **************************************************************/
			synchronized private boolean shift(HostList<T> otherList, int port){
				// already in this list
				if(get(port)!=null)
					return true;
				// can't move him over, so leave him in this list
				if(atCapacity())
					return false;
				//serverStream.println("SERVER> PULLING CLIENT: <"+port+">");				
				T c=otherList.pull(port);
				//serverStream.println("SERVER> PULLED "+c);
				if(this.queue(c)){
					serverStream.println("SERVER> SHIFTED "+c);
					return true;
				}
				serverStream.println("SERVER> FAILED TO QUEUE "+c);
				return false;
			}/*</shift>*/
			/***************************************************************
			 * Move client from the other list to this list.
			 * @param otherList
			 * @param port client's port no
			 * @return Whether exchange successful, failure most likely
			 * due to over population.
			 **************************************************************/
			synchronized private boolean shift(HostList<T> otherList, T client){
				if(get(client)!=null)
					return true;
				if(atCapacity())
					return false;
				//serverStream.println("SERVER> PULLING CLIENT: "+client);
				T c=otherList.pull(client);
				//serverStream.println("SERVER> PULLED "+c);
				if(this.queue(c)){
					serverStream.println("SERVER> SHIFTED "+c);
					return true;
				}
				serverStream.println("SERVER> FAILED TO QUEUE "+c);
				return false;
			}/*</shift>*/
			/***************************************************************
			 * Return the client identified by the port
			 * @param the port
			 * @return the client
			 **************************************************************/
			public T get(int port){
				for(int i=0;i<line.size();i++)
					if(port==line.get(i).port())
						return line.get(i);
				return null;
			}
			/***************************************************************
			 * Return the client identified by the uid
			 * @param the uid
			 * @return the client
			 **************************************************************/
			public T get(String uid){
				if(uid==null)
					return null;
				for(int i=0;i<line.size();i++)
					if(uid.equals(line.get(i).uid()))
						return line.get(i);
				return null;
			}/*</get>*/
			/***************************************************************
			 * Return the client identified by the object.
			 * @param the instance
			 * @return the client
			 **************************************************************/
			public T get(Client c){
				for(int i=0;i<line.size();i++)
					if(c==line.get(i))
						return line.get(i);
				return null;
			}/*</get>*/
			/***************************************************************
			 * Lets us know if the room is full
			 * @return no more space
			 **************************************************************/
			private boolean atCapacity(){
				return (!(CAPACITY>line.size()));
			}/*</atCapacity>*/
			/***************************************************************
			 * Readonly method returns updated list of users.  Better if this
			 * structure is a static list effectively maintained by the server,
			 * but effeciency not a problem of interest yet.
			 * @return space delimited list of users
			 **************************************************************/
			private String whoison(){
				String list=" ";
				for(int i=0;i<line.size();i++){
					Client c=line.get(i);
					list = list + c.uid() + ":" + c.port() + " ";
				}
				return list;
			}/*</whoison>*/
			/***********************************************************************
			 * HostList is given the job of broadcasting a packet in order to cope
			 * with persistence issues.
			 * *******************************************************************/
			private void broadcast(Packet q){
				for(int i=0;i<party.size();i++)
					line.get(i).transmit(q);
			}/*</broadcast>*/
		}/*</HostList>*/
		/*******************************************************************
		 * user:password pair for the server's primary data structure.
		 ******************************************************************/
		final class Key{
			final String uid;
			final String password;
			private Key(String uid, String password){
				this.uid=uid;
				this.password=password;
			}/*</Key>*/
		}/*</Key>*/
	}/*</Server>*/
	/*****************************************************************************
	* Blocks for client connections from TCP, new socket immediately handed to
	* factory client constructor which puts it into the wait list.
	******************************************************************************/
	class DoorMan extends Thread{
		/** Status messages */
		PrintStream doorStream;
		/** Welcome socket abstraction */
		ServerSocket welcomeSocket;
		DoorMan(){
			doorStream = System.out;
			doorStream.println("\nDOORMAN> INITIALIZING...");
			try{
				doorStream.println("\nDOORMAN> OPENING WELCOME SOCKET... port=" + WELCOMEPORT);
				welcomeSocket = new ServerSocket(WELCOMEPORT);
				doorStream.println(
					"\nDOORMAN> WELCOME SOCKET OPENED" +
					"\n\tinet address: " + welcomeSocket.getInetAddress()+
					"\n\tlocal port: " + welcomeSocket.getLocalPort()+
					"\n\tlocal socket address: " + welcomeSocket.getLocalSocketAddress()
				);
			}
			catch(IOException ioEx){
				ioEx.printStackTrace();
				doorStream.println(
					"\nDOORMAN>...UNABLE TO ATTACH port=" + WELCOMEPORT +
					"\nCHECK TO SEE IF PORT IS OCCUPIED.\n EXITING...");
				System.exit(1);
			}
			catch(Exception ex){
				System.out.println("\n\nDOORMAN>... FAILED TO INITIALIZE, EXITING");
				ex.printStackTrace();
				System.exit(1);
			}
		}/*</DoorMan>*/
		/*************************************************************************
		 * Indefinite looping process to get a new user in the queue to join
		 * the conversation.  Process blocks, so I don't have to worry about
		 * concurrency here.
		 *************************************************************************/
		public void run(){
			while(running){
				try{
					Socket newHost = alloc(welcomeSocket.accept());
					doorStream.println(
						"\nDOORMAN>\tCONNECTION ESTABLISHED WITH "
						+"\n\t\tip=" + newHost.getInetAddress()
						+"\n\t\tport=" + newHost.getPort()
						+"\n\t\tlocalport=" + newHost.getLocalPort()
					);
				}/*</try>*/
				catch(Exception e){
					doorStream.println(
						"\nDOORMAN> ENCOUNTERED A PROBLEM WHILE RUNNING");
					e.printStackTrace(doorStream);
					/*System.exit(1);*/
				}/*</catch>*/
			}/*</while>*/
		}/*</run>*/
		/*****************************************************************************
		 * Sudo factory method for client.
		 * if this is static method in client 
		 * 		=> it does not belong to an instance of server,
		 * therefore cannot access the ASSServer instance members, 
		 * 		so it must not be a member of client or it can't be called
		 *****************************************************************************/
		private Socket alloc(Socket link){
			Client c = new Client(link);
			if(!server.push(c))/* put into queue */
				doorStream.println(
					"\nDOORMAN> CLIENT "+c.toString()+" IS NOW WAITING ");
			c.start();
			return link;
		}/*</alloc>*/
	}/*</DoorMan>*/
	/*****************************************************************************
	* Emulates the client's existence using the socket's input and output streams.
	* 
	* Client does not need a listener class, it can get away with extending thread
	* and having a run method which is only called by the server, etc
	******************************************************************************/
	private class Client extends Thread{
		/*<UNIQUE>*/
		private final Socket socket; /*  Client's abstract connection to server, client will never get another socket */
		private final DataOutputStream cout;/* Should never get another output stream, property of the socket */
		private final BufferedReader cin;/* Should never get another input stream, property of the socket */
		/*</UNIQUE>*/
		/*<INSTANCE>*/
		/*****************************************************************************
		 * Mutable reference.
		 * Obtatined from login packet, assinged by server authentication.
		 * A null reference implies the user is not logged in to the server.
		 ****************************************************************************/
		protected String uid=null;
		/*</INSTANCE>*/
		/*****************************************************************************
		* Must have TCP socket constructed in order to build a client.
		******************************************************************************/
		private Client(Socket socket){
			this.socket=socket;
			DataOutputStream cout=null;
			BufferedReader cin=null;
			try{
				socket.setReceiveBufferSize(MAXBUFFERLENGTH);
				cout=new DataOutputStream(socket.getOutputStream());
				cin=new BufferedReader(new InputStreamReader(socket.getInputStream()));
			}
			catch(Exception e){
				stderr.println("{"+socket.getPort()+"}"+"> FAILED TO CONSTRUCT CLIENT");
				e.printStackTrace();
			}
			this.cin=cin;
			this.cout=cout;
		}/*</Client>*/
		/** Dummy client */
		private Client(){
			socket=null;
			cout=null;
			cin=null;
		}
		/***********************************************************************
		 * Listening process starts listening to the client and interpreting its input
		 * as packets.  Getting a packet blocks for input.
		 **********************************************************************/
		public void run(){
			preamble();
			while(running && socket.isConnected() && !socket.isClosed())
				get();
			stderr.println(
				"\n<"+socket.getPort()+"> THREAD TERMINATED"
			);
		}/*</run>*/
		private void preamble(){
			transmit(new Packet("connect","admin","Welcome, you have been queued in the waitlist"));
		}/*</preamble>*/
		/***********************************************************************
		 * Get and send the next packet to server.
		 * If the attempt to push packet to the server is unsuccessful, a
		 * disconnect request is sent to the server is sent on behalf of the
		 * client.
		 **********************************************************************/
		protected void get(){
			try{
				server.push(new ServerPacket(cin,this));
			}
			catch(Exception e){
				/* Likely because client did not appropriately disconnect */
				stderr.println(
					"\n<"+socket.getPort()+"> EXCEPTION THROWN, TERMINATING CLIENT"
				);
				e.printStackTrace(stderr);
				try{
					socket.close();
				}
				catch(Exception f){
					f.printStackTrace(stderr);
				}
				server.push(new ServerPacket("disconnect","","",this));
			}
		}/*</get>*/
		/***********************************************************************
		 *  Put packet response in client's stream.
		 *     synchronized?
		 **********************************************************************/
		public void transmit(Packet r){
			try{
				cout.writeBytes(r.toString());
			}
			catch(Exception e){
				/* I Assume this occurs because
				 *  socket is closed, this method should do something about it*/
				stderr.println(
					"\n<"+socket.getPort()+"> ERROR: COULD NOT SEND PACKET\n"
					+ r.toDelimitedString()
				);
				stderr.println(e.getCause());
			}
		}/*</transmit>*/
		/*****************************************************************************
		 * Bian: "yes" => Each client port number is unique
		 * @return the client's port no
		 **********************************************************************/
		public int port(){
			return socket.getPort();
		}
		/***********************************************************************
		 * To be called by server indicating the client is authenticated
		 * and put into the party.
		 **********************************************************************/
		public void authenticate(String uid){
			transmit(new Packet("login","admin","acknowledged, WELCOME"));
			this.uid=uid;
		}/*</authenticate>*/
		/***********************************************************************
		 * To be called by server indicating the client is not in the party,
		 * and does not have a uid associated.
		 **********************************************************************/
		public void deauthenticate(){
			deauthenticate("YOU ARE NOT LOGGED IN");
		}/*</deauthenticate>*/
		/***********************************************************************
		 * To be called by server indicating the client is not in the party,
		 * and does not have a uid associated.
		 * @param body status message to be sent to end user
		 **********************************************************************/
		public void deauthenticate(String body){
			uid=null;
			transmit(new Packet("logout","admin",body));
		}/*</deauthenticate>*/
		/***********************************************************************
		 * End client process.  Catch exception in case several processes try
		 * to kill it at the same time.
		 **********************************************************************/
		public void kill(){
			try{
				transmit(new Packet("error","admin","FATAL ERROR OCCURED, YOU ARE BEING DISCONNECTED"));
			}
			catch(Exception e){
				//stderr.println(e.getCause());
			}
			try{
				socket.close();
				this.interrupt();
			}
			catch(Exception e){
				stderr.println("<"+socket.getPort()+"> EXCEPTION THROWN  DURING KILL");
				e.printStackTrace(stderr);
			}
		}/*</kill>*/
		/***********************************************************************
		 * Each universal identifier must be unique.
		 * Just discovered that private may actually mean something in this program
		 * @return the client's uid.
		 **********************************************************************/
		public String uid(){
			return uid;
		}/*</uid>*/
		/***********************************************************************
		 * Unique representation of the client
		 * @return the port number to a string
		 **********************************************************************/
		public String toString(){
			return "<"+port()+">";
		}/*</toString>*/
	}/*</Client>*/
	/**********************************************************************
	 * Dummy class for artificial client process, although it could be 
	 * practical in the future to actually have an administrator for a
	 * session.
	 **********************************************************************/
	private class Admin extends Client{
		public Admin(){
			super();
			uid="admin";
		}
		public void authenticate(String uid){
			;
		}
		public void deauthenticate(){
			;
		}
		public void deauthenticate(String body){
			;
		}
		public void kill(){
			;
		}
		protected void get(){
			;
		}
		public void run(){
			;
		}
		public void transmit(Packet r){
			;
		}
		public int port(){
			return 0;
		}
	}/*</Admin>*/
	/*******************************************************************
	 * Packet in server is associated with a client.
	 * Tokens are space delimited
	 * <command> <uid> CRLF
	 * <body> CRLF
	 ******************************************************************/
	private class ServerPacket extends Packet{
		/***********************************************************************
		 * Packet with a return address, a reference to client.
		 **********************************************************************/
		final Client c;
		private ServerPacket(BufferedReader cin,Client c)throws Exception{
				super(cin);
				this.c=c;
		}/*</ServerPacket>*/
		/***********************************************************************
		 * Packet with a return address, a reference to client.
		 **********************************************************************/
		private ServerPacket(Packet p,Client c){
			super(p.command,p.uid,p.body);
			this.c=c;
		}/*</ServerPacket>*/
		/***********************************************************************
		 * Packet with a return address, a reference to client.
		 **********************************************************************/
		private ServerPacket(String command, String uid, String body,Client c){
			super(command,uid,body);
			this.c=c;
		}/*</ServerPacket>*/
		/***********************************************************************
		 * Makes a simple packet by cutting client information
		 **********************************************************************/
		private Packet toPacket(){
			return new Packet(command,uid,body);
		}/*</toPacket>*/
	}/*</ServerPacket>*/
}/*</ASSServer>*/
